import {
	unstable_ImmediatePriority as ImmediatePriority,
	unstable_UserBlockingPriority as UserBlockingPriority,
	unstable_NormalPriority as NormalPriority,
	unstable_LowPriority as LowPriority,
	unstable_IdlePriority as IdlePriority,
	unstable_scheduleCallback as scheduleCallback,
	unstable_shouldYield as shouldYield,
	CallbackNode,
	unstable_getFirstCallbackNode as getFirstCallbackNode,
	unstable_cancelCallback as cancelCallback
} from 'scheduler';

import './style.css';
const button = document.querySelector('button');
const root = document.querySelector('#root');

type Priority =
	| typeof IdlePriority
	| typeof LowPriority
	| typeof NormalPriority
	| typeof UserBlockingPriority
	| typeof ImmediatePriority;

interface Work {
	count: number;
	priority: Priority;
}

const workList: Work[] = [];
let prevPriority: Priority = IdlePriority;
let curCallback: CallbackNode | null = null;

[LowPriority, NormalPriority, UserBlockingPriority, ImmediatePriority].forEach(
	(priority) => {
		const btn = document.createElement('button');
		root?.appendChild(btn);
		btn.innerText = [
			'',
			'ImmediatePriority',
			'UserBlockingPriority',
			'NormalPriority',
			'LowPriority'
		][priority];
		btn.onclick = () => {
			workList.unshift({
				count: 100, //count 代表着 就是  一个任务里面 有多少个组件需要处理
				priority: priority as Priority
			});
			schedule();
		};
	}
);

function schedule() {
	const cbNode = getFirstCallbackNode();
	// 排序 优先级高的在前面
	const curWork = workList.sort((w1, w2) => w1.priority - w2.priority)[0];

	// 策略逻辑
	if (!curWork) {
		curCallback = null;
		cbNode && cancelCallback(cbNode);
		return;
	}

	const { priority: curPriority } = curWork;
	if (curPriority === prevPriority) {
		// 如果是 空闲优先级的 话就不执行
		return;
	}
	// 更高优先级的work
	cbNode && cancelCallback(cbNode);

	// 调度返回一个 回调汗水
	curCallback = scheduleCallback(curPriority, perform.bind(null, curWork));
}

function perform(work: Work, didTimeout?: boolean) {
	// 什么情况下中断 什么情况不中断 根据下面三个条件来判断
	/**
	 * 1. work.priority 如果任务本身优先级 就是一个 同步的 那么就不能中断
	 * 2. 饥饿问题 如果一个任务 一直得不到执行 优先级竞争不过别的任务 给任务设置过期时间 如果过期了 他的优先级就变成 同步任务  同步任务优先级是最高的
	 * 3. 时间切片  shouldYield 表示 需要切片了 就是没时间 了
	 */
	const needSync = work.priority === ImmediatePriority || didTimeout;
	while ((needSync || !shouldYield()) && work.count) {
		// 继续执行的 条件 任务没执行完 且 (是一个同步任务 或者 时间切片没到)
		// 如果 没时间了 但任务是 同步的 那么也需要继续执行
		work.count--;
		insertSpan(work.priority + '');
	}

	// 中断执行 或者 执行完  了 就记录下  任务的优先级
	prevPriority = work.priority;

	if (!work.count) {
		// 任务执行完了
		const workIndex = workList.indexOf(work);
		workList.splice(workIndex, 1); // 从列表中删除任务
		// 如果是 执行完了 就重置优先级
		prevPriority = IdlePriority;
	}

	const prevCallback = curCallback;
	schedule();
	const newCallback = curCallback;

	if (newCallback && prevCallback === newCallback) {
		// 工作过程仅有一个work
		// 如果仅有一个work，Scheduler有个优化路径：如果调度的回调函数的返回值是函数，则会继续调度返回的函数。
		return perform.bind(null, work);
	}
}

function insertSpan(content) {
	const span = document.createElement('span');
	span.innerText = content;
	span.className = `pri-${content}`;
	doSomeBuzyWork(10000000);
	root?.appendChild(span);
}

function doSomeBuzyWork(len: number) {
	// 人为降低速度
	let result = 0;
	while (len--) {
		result += len;
	}
}
